/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * $Id: TextData.cpp,v 1.7 2009-07-20 08:47:24 morsko Exp $
 *
 * \file
 * \brief Text interface data encoder/decoder implementation.
 * \author Matthieu Myrsky <matthieu.myrsky@tkk.fi>
 */
#include "TextData.hpp"
#include "binbag.h"
#include "owndebug.h"
#include <stdio.h>

//*****************************************************************************
//*****************************************************************************
using namespace MaCI::Text;
//*****************************************************************************
//*****************************************************************************

CTextData::CTextData(void):
  iValid(false),
  iCommandPtr(NULL),
  iTextPtr(NULL),
  iTextHeaderPtr(NULL),
  iTextElementCount(0),
  iHeaderTextElementCount(0)
{

}
//*****************************************************************************

CTextData::~CTextData(void)
{

}
//*****************************************************************************      
      
CTextData::CTextData(const CTextData &aTextData):
  iValid(false),
  iCommandPtr(NULL),
  iTextPtr(NULL),
  iTextHeaderPtr(NULL),
  iTextElementCount(0),
  iHeaderTextElementCount(0)
{
  *this = aTextData;
}
//*****************************************************************************
            
CTextData &CTextData::operator=(const CTextData &aTextData)
{
 // Reset
  Reset();
  // Create an internal BinBag instance
  if (CreateInternalBinBag(aTextData.iBinBagContainer) == false) {
    dPrint(4,"WARNING: Failed to create internal binbag for object %p from object %p",
           this, &aTextData);
  }
  
  // Do the decode on the buffer; it sets pointers and
  if (DecodeFrom() == false) {
    dPrint(4,"WARNING: Failed to Decode TextData even though assignment operator used?");
  }
  
  return *this; 
  
}
//*****************************************************************************
     
bool CTextData::DecodeFrom(gim::binbag::CBinBag *aBinBag)
{

 bool result = true;
  const gim::binbag::TBinBlob *b = NULL;
  int previous_type = -1;
  
  // Store pointer and test it for NullNess
  if (aBinBag != NULL)  
    iBinBagContainer = aBinBag;
  
  // Parse
  if (iBinBagContainer && iBinBagContainer->Rewind()) {
    while( (b = iBinBagContainer->GetNextBinary()) && result ) {
      switch(b->type) {
      case KTypeText:
	if(iTextPtr ==NULL && iTextHeaderPtr != NULL&&
	   previous_type == KTypeTextHeader){
	  iTextPtr = reinterpret_cast<const TText *>(b->data);
	  iTextElementCount++;
	}else if(iTextPtr == NULL){
	  iTextPtr = reinterpret_cast<const TText *>(b->data);
	  iTextElementCount++;
	}else if(iTextPtr != NULL &&previous_type == KTypeText){
	  iTextElementCount++;
	}else{

	  result = false;
	}
	break;

      case KTypeCommand:
        if (iCommandPtr == NULL) {
          iCommandPtr = reinterpret_cast<const TCommand *>(b->data);
        } else {

	  result = false;
	}
        break;

      case KTypeTextHeader:
        if (iTextHeaderPtr == NULL) {
          iTextHeaderPtr = reinterpret_cast<const TTextHeader *>(b->data);
          iHeaderTextElementCount = iTextHeaderPtr->number;
        } else{

	  result = false;
	}
        break;

      default:
	// Unknown Type in BinBag. Do nothing.
	break;
      }
      
      // Store previous type
      previous_type = b->type;
    }
  } else result = false;
  
  // Store the result
  SetReadOnlyContainer((iValid = result));
  
  return result;
}
//*****************************************************************************

void CTextData::Reset()
{
  CMaCIData::Reset();
  
 // Clean out. (Same values as in default constructor)
  iValid = (false);

  iCommandPtr = (NULL);
  iTextPtr = (NULL);
  iTextHeaderPtr = (NULL);
  iTextElementCount= (0);
  iHeaderTextElementCount= (0);
  SetLastElementType(KTypeUnknown);
}
//*****************************************************************************

void CTextData::Print(const int level) const
{
  const gim::binbag::TBinBlob *b = NULL;
  if(iValid){
    dPrint(level,"TextData is valid ");
  }else{
    dPrint(level,"TextData isn't valid,something is wrong!");
  }
  // Parse
  if (iBinBagContainer && iBinBagContainer->Rewind()) {
    
    while( (b = iBinBagContainer->GetNextBinary())) {
      switch(b->type) {
      case KTypeCommand: {
        const TCommand *cp = reinterpret_cast<const TCommand *>(b->data);
        dPrint(level,"TCommand: %d",
               cp->cmd);
        break;
      }

      case KTypeTextHeader:{
        const TTextHeader *h = reinterpret_cast<const TTextHeader *>(b->data);
        dPrint(level,"TTextHeader: number of TText-structure %d",
               h->number);
        break;
      }

      case KTypeText:{
        const TText *t = reinterpret_cast<const TText *>(b->data);
        dPrint(level,"TText : '%s'", t->text);
        break;
      }
      default:
	// Unknown Type in BinBag. Do nothing.
	break;
      }
    }
  }
}
//*****************************************************************************
   
bool CTextData::SetCommand(const TCommand &aCmd){
  
  bool result = false;
  if (!IsReadOnlyContainer() && iCommandPtr == NULL) {
    if(GetLastElementType() == KTypeUnknown || iValid){
      // Use the handy Macro found from MaCIData
      ADDELEMENT(TCommand, KTypeCommand, iCommandPtr, aCmd);
      result = iValid = true;
      
    }else{
      dPrint(3,"There is something wrong at TextData, can't add Command. Last element type %d, Valid %d",
             GetLastElementType(),
             iValid);
    }
  }
  return result;
}
//*****************************************************************************

bool CTextData::SetText(const TText &aText){
  bool result = false;
  if (!IsReadOnlyContainer()) {
    //If there is an header:
    // Check if there is enough text-elements, 
    // if yes print warning and don't add it, if no Add text-element and increase the element count
    // 
    if(iTextHeaderPtr != NULL){
      if (iHeaderTextElementCount == iTextElementCount){
        dPrint(3,"Warning!There is already as much TText-structure elements than said in TextHeader(%d), can't add anymore",iHeaderTextElementCount);
      }else{
        const gim::binbag::TBinBlob *b = 
          iBinBagContainer->AddBinary(KTypeText, (const char *)&aText, 
                                      sizeof(TText));
        
        if (iTextPtr == NULL) {
          iTextPtr = reinterpret_cast<const TText *>(b->data);
          SetLastElementType(KTypeText);
        }
        
        // Increment element count
    
        ++iTextElementCount;
        //if there is enough AFTER adding element, CoordinateDriveData IS valid
        if (iHeaderTextElementCount == iTextElementCount) iValid = true;
        else iValid = false;
        result = true;
      }


    }else{
      dPrint(3,"No header found. Add a TTextHeader first!"); 
    }
   
  }else{
    dPrint(2,"TextData is Read only!");
  }
     
  return result;

}
//*****************************************************************************

bool CTextData::SetText(const std::string &aText){
  bool result = true;

  if(aText.size() %31 != 0){
    result = SetTextHeader(TTextHeader((aText.size()/31) +1));
  }else{
    result = SetTextHeader(TTextHeader((aText.size()/31)));
  }
  dPrint(10,"Setting header, result = %d",result);

  for(unsigned int i = 0; i <aText.size() && result == true; i +=31){

    std::string subStr = aText.substr(i,31);
    dPrint(16,"Substring is '%s'",subStr.c_str());
    TText textStruct;
    textStruct.AddText(subStr);
    result = SetText(textStruct);
    dPrint(10,"Setting text, result = %d",result);
  }

  return result;




}
//*****************************************************************************

bool CTextData::SetTextHeader(const TTextHeader &aHeader)
{
 bool result = false;

  if (!IsReadOnlyContainer() && iTextHeaderPtr == NULL) {
    
    // Use the handy Macro found from MaCIData
    if(GetLastElementType() == KTypeUnknown || iValid){
      ADDELEMENT(TTextHeader, KTypeTextHeader, iTextHeaderPtr, aHeader);
      iHeaderTextElementCount = aHeader.number;
      dPrint(10,"header element coubnt %d",iHeaderTextElementCount);
      result = true;
      if(aHeader.number != 0){
        iValid = false;
      }else{
        iValid = true;
      }
      SetLastElementType(KTypeTextHeader);

    }else{
      dPrint(3,"There is something wrong at TextData, can't add TextHeader. Last element type %d, Valid %d",
             GetLastElementType(),
             iValid);
    }
  }
  return result;

}
//*****************************************************************************
bool CTextData::GetWholeText(std::string &aText)
{
  aText.clear();
  for(unsigned int i = 0; i < iHeaderTextElementCount; i++){
    if(GetText(i)->text == NULL){
      return false;
    }
    aText += std::string(GetText(i)->text);
  }
  return true;

}
//*****************************************************************************

bool CTextData::IsValid()
{
  return iValid;
}
//*****************************************************************************
#ifdef COMPILE_TEXTDATA_STANDALONE_MAIN
int main(void)
{

  debugInit();
  gim::binbag::CBinBag bb;
  dPrint(1,"Hello World!");
  CTextData A;
  CTextData B;

  printf("Carry out Encoding/Decoding test.\n");

  printf("Encoding...\n");
  // Do Encoding 
  A.EncodeTo(&bb);

  printf("Encode: Setting Command(SendText (%d)).\n",KCommandSendText);
  A.SetCommand(TCommand(KCommandSendText));
  printf("OK.\n");

  //Old style, there's an easier way now.
  /* int numberOfTextElement = 2;

  printf("Encode: Setting TextHeader(number (%d)).\n",numberOfTextElement);
  A.SetTextHeader(TTextHeader(numberOfTextElement));
  printf("OK.\n");

    std::string firstText= "**********Hello world!*********";
  TText fTT;
  
  fTT.AddText(firstText);

  std::string secondText=   "*******Is this working?********";
  TText sTT;
  sTT.AddText(secondText);

  printf("Encode: Setting text('%s').\n",firstText.c_str());
  A.SetText(fTT);
  printf("OK.\n");
  printf("Encode: Setting text('%s').\n",secondText.c_str());
  A.SetText(sTT);
  printf("OK.\n");
  */

  std::string textToAdd = "**********Hello world1*******************Hello world2*******************Hello world3*******************Hello world4*********";
                                                                                                               
  A.SetText(textToAdd);
  printf("Let's print TextData (A)..\n");
  A.Print(1);

 
  printf("-----------------------------------------------------\n");
  printf("-----------------------------------------------------\n");
  printf("Decoding...\n");
  // Do Decoding
  B.DecodeFrom(&bb);
  if(B.IsValid()){

    printf("TextToData is Valid, let's decode it\n");
    printf("Decode: Command (%d)\n",
           B.GetCommand()->cmd);

    printf("Decode: Text header (number of elements (%d)).\n",
           B.GetTextHeader()->number);

    for(unsigned int i = 0; i< B.GetTextHeader()->number; i++){
      printf("Decode: Text: '%s'.\n",
             B.GetText(i)->text);
    }

  }else{
    printf("TextData (B) is not Valid!\n");
    B.Print(1);
  }
  printf("-----------------------------------------------------\n");
  printf("-----------------------------------------------------\n");
  CTextData C(A); 
  printf("Trying the copy constructor..\n");
  printf("Print data:\n");
  //C.Print(1);
  if(C.IsValid()){
    printf("TextToData is Valid, let's decode it\n");
    printf("Decode: Command (%d)\n",
           C.GetCommand()->cmd);
    printf("Decode: Text header (number of elements (%d)).\n",
           C.GetTextHeader()->number);
    std::string s;
    C.GetWholeText(s);
    printf("Decode: Text: '%s'.\n",s.c_str());

  }
  printf("-----------------------------------------------------\n");
  printf("-----------------------------------------------------\n");
  CTextData D = C;
  printf("Trying the = operator\n");
  printf("Print data:\n");
  D.Print(1);
  printf("\nTest complete.\n");
  return 0;
}
#endif
